"""
路径辐射度采样器
"""
# pylint: disable=unused-wildcard-import
# pylint: disable=wildcard-import,invalid-name,missing-class-docstring,missing-function-docstring
import taichi as ti
from .gtaichi import *
from .material import (bxdf,bxdf_weight)

@ti.data_oriented
class RayTraceIntegrator:
    """
    使用蒙特卡洛估计路径中的辐射度
    路径由一系列的光线碰撞点组成p1,p2,p3,...,将每个点上采集到的辐射度累加得到路径辐射度
    L = L1 + L2*w1 + L3*w1*w2 + ...
    w1,w2,w3,...对应于碰撞点p1,p2,p3,...的衰减权重(因为能量守恒权重都在0-1范围内)
    可以看出随着碰撞的增多权重将越来越小,代表在路径末端采集到的光对结果影响越来越低.
    详细信息看doc/light_transport_equation.md
    """
    def __init__(self,render,depth=8):
        self.depth = depth
        self.render = render

    @ti.func
    def l_i(self,ray):
        """
        返回路径辐射度Radiance
        """
        bounding = self.render.bounding
        weight = ti.Vector([1.,1.,1.])
        L = ti.Vector([0., 0., 0.])
        for bounce in range(self.depth):
            b,ints = bounding.intersection(ray)
            if b:
                m = bounding.get_material(ints.m)
                c = bounding.get_object_color(ints)
                v,le,d = bounding.light_sample(ints)
                if v:
                    f = bxdf_weight(d,-ray.direct,ints,m,c)*ti.math.dot(d,ints.n)
                    L += le*weight*ti.min(f,np.pi)
                    #使用俄罗斯轮盘赌随机退出循环,weight越小退出的概率越大
                    q = ti.max(ti.max(weight.x,weight.y),weight.z)
                    if ti.random() < 1.-q:
                        break

                w,ray = bxdf(-ray.direct,ints,m,c)
                weight *= w
            else:
                L += weight*bounding.environment_sample(ray)
                break
        return L
